package com.wl.cloud.security.config;

import com.wl.cloud.security.bean.IgnoreUrlsProperties;
import com.wl.cloud.security.component.JwtAccessDeniedHandler;
import com.wl.cloud.security.component.JwtAuthenticationEntryPoint;
import com.wl.cloud.security.component.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

/**
 * @author: wanglin
 * @date: 2023-11-08 周三
 * @Version: 1.0
 * @Description:
 */
@Configuration
@EnableWebSecurity
public class CommonSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private IgnoreUrlsProperties ignoreUrlsProperties;
    @Autowired
    private JwtAccessDeniedHandler jwtAccessDeniedHandler;
    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    /**
     * 实现自己的登录逻辑
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //指定用户认证时，默认从哪里获取认证用户信息
        auth.userDetailsService(userDetailsService);
//        auth.userDetailsService(userDetailsService()).passwordEncoder(bCryptPasswordEncoder());
    }

    /**
     * ignore是完全绕过了spring security的所有filter，相当于不走spring security，所以ignore只适合配置静态资源的过滤，不适合api的过滤，过滤api就调用不了api了
     * 静态资源路径直接放行
     *
     * @param web
     */
    @Override
    public void configure(WebSecurity web) {
//        web.ignoring().antMatchers(ignoreUrlsProperties.getUrls().stream().toArray(String[]::new));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
        // 不需要保护的资源路径允许访问，permitAll 没有绕过spring security，其中包含了登录的以及匿名的
//        for (String url : ignoreUrlsConfig.getUrls()) {
//            registry.antMatchers(url).permitAll();
//        }
        ignoreUrlsProperties.getUrls().stream().forEach(e -> registry.antMatchers(e).permitAll());
        registry
                .and()
                // 禁用 CSRF
                .csrf().disable()
//                .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
                .authorizeRequests()
                // 允许跨域的OPTIONS请求
                .antMatchers(HttpMethod.OPTIONS).permitAll()
//                .accessDecisionManager(dynamicAccessDecisionManager)
                // 放行登录、注册等
//                .antMatchers("/**/common/**").permitAll()
//                .antMatchers("/**/user/page").hasAnyAuthority("admin","user")
                //只有有这个权限才能访问
//                .antMatchers("/**/user/page").hasAnyAuthority("ADMIN", "COMMON_USER")
//                .antMatchers("/**/user/list").hasAnyAuthority("COMMON_USER")
//                .antMatchers("/**/user/get").hasAnyAuthority("ADMIN")
//                .antMatchers("/**/user/get").hasAnyRole("ADMIN")
                // 自定义匿名访问所有url放行 ： 允许匿名和带权限以及登录用户访问
//                .antMatchers(anonymousUrls.toArray(new String[0])).permitAll()
//                .antMatchers(ignoreUrlsProperties.getApis().toArray(new String[0])).permitAll()
                // 其他任何请求都需要身份认证
                .anyRequest().authenticated()
//                .accessDecisionManager(dynamicAccessDecisionManager)
                .and()
//                .addFilter(new JwtAuthenticationFilter(authenticationManager()))
//                .addFilter(new JwtAuthorizationTokenFilter(authenticationManager()))
                // 不需要session,不创建会话
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
//                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
//                .and()
                //过滤器在SecurityFilterChain之前执行
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
                // 添加自定义未授权和未登录返回结果
                .exceptionHandling()
                //登录状态下，操作权限不足
                .accessDeniedHandler(jwtAccessDeniedHandler)
                //未登录
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                // 防止iframe 造成跨域
                .and()
                .headers()
                .frameOptions()
                .disable();
        //有动态权限配置时添加动态权限校验过滤器
//        if (dynamicSecurityService() != null) {
//            registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
//        }
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }

    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }
}
